#include "HttpServer.h"
#include "Util.h"
#include "Logger.h"
#include "Connection.h"
#include "HttpContext.h"

void HttpServer::ErrorHandler(const HttpRequest &req, HttpResponse *rsp)
{
    // 1. 组织一个错误展示页面
    std::string body;
    body += "<html>";
    body += "<head>";
    body += "<meta http-equiv='Content-Type' content='text/html;charset=utf-8'>";
    body += "</head>";
    body += "<body>";
    body += "<h1>";
    body += std::to_string(rsp->_statu);
    body += " ";
    body += Util::StatuDesc(rsp->_statu);
    body += "</h1>";
    body += "</body>";
    body += "</html>";
    // 2. 将页面数据，当做响应正文，放入rsp中
    rsp->SetContent(body, "text/html");
}

// 将HttpResponse中的要素按照http协议格式进行组织，发送
void HttpServer::WriteResponse(const PtrConnection &conn, const HttpRequest &req, HttpResponse &rsp)
{
    // 1. 先完成头部字段 --- 这几个是几乎必要的头部字段
    if (req.Close() == true)
        rsp.SetHeader("Connection", "close");
    else
        rsp.SetHeader("Connection", "keep-alive");
    if (rsp._body.empty() == false && rsp.HasHeader("Content-Length") == false) //如果没有设置长度，则要设置长度
        rsp.SetHeader("Content-Length", std::to_string(rsp._body.size()));
    if (rsp._body.empty() == false && rsp.HasHeader("Content-Type") == false) //如果没有设置Content-Type，则要设置
        rsp.SetHeader("Content-Type", "application/octet-stream");
    if (rsp._redirect_flag == true)
        rsp.SetHeader("Location", rsp._redirect_url);
    // 2. 将rsp中的要素， 按照http协议格式进行组织
    std::stringstream rsp_str;
    rsp_str << req._version + " " << std::to_string(rsp._statu) << " " << Util::StatuDesc(rsp._statu) << "\r\n";
    for (auto &head : rsp._headers)
    {
        rsp_str << head.first << ":" << head.second << "\r\n";
    }
    rsp_str << "\r\n";
    rsp_str << rsp._body;
    // 3. 发送数据
    conn->Send(rsp_str.str().c_str(), rsp_str.str().size());
}

// 是否是一个静态资源请求
bool HttpServer::IsFileHandler(const HttpRequest &req)
{
    // 1. 必须设置了静态资源根目录
    if (_basedir.empty())
        return false;
    // 2. 请求方法必须是GET/HEAD方法 -- 只有这2个才是获实体资源的请求 POST/POT/DELETE通常是更多功能性的请求
    if (req._method != "GET" && req._method != "HEAD")
        return false;
    // 3. 请求资源路径必须是一个合法的路径
    if (Util::ValidPath(req._path) == false)
        return false;
    // 4. 请求的资源必须存在，且是一个普通文件
    std::string req_path = _basedir + req._path;
    if (req._path.back() == '/')
        req_path += "index.html";
    if (Util::IsRegulay(req_path) == false) // 是否是普通文件
        return false;
    return true;
}

// 静态资源的请求处理
void HttpServer::FileHandler(const HttpRequest &req, HttpResponse *rsp)
{
    std::string req_path = _basedir + req._path;
    if (req._path.back() == '/')
    {
        req_path += "index.html";
    }
    bool ret = Util::ReadFile(req_path, &(rsp->_body));
    if (ret == false)
        return;
    std::string mime = Util::ExtMime(req_path); // 通过扩展名来获取它的mime
    rsp->SetHeader("Content-Type", mime);
    return;
}

// 功能性请求的分类处理
void HttpServer::Dispatcher(HttpRequest &req, HttpResponse *rsp, Handlers &handlers)
{
    // 在对应请求方法的路由表中，查找对应是否含有对应资源请求的处理函数，有则调用，没有则返回404
    // 思想：路由表存储的是键值对<正则表达式, 处理函数>
    // 使用正则表达式，对请求的资源路径进行正则匹配，匹配成功就使用对应函数进行处理
    // 为什么用正则表达式，而不用字符串？因为查询路径，可能有很多种情况，我们不能全写进路由表
    for (auto &handler : handlers) // 将每一个正则表达式拿出来进行宇查询路径进行匹配
    {
        const std::regex &re = handler.first;
        const Handler &functor = handler.second;
        bool ret = std::regex_match(req._path, req._matches, re);
        if (ret == false)
            continue;
        return functor(req, rsp); // 传入请求信息和空的rsp，执行处理函数
    }
    rsp->_statu = 404;
}

void HttpServer::name(const HttpRequest &req, HttpResponse *rsp)
{
    std::string req_path = _basedir + req._path + ".html";
    bool ret = Util::ReadFile(req_path, &(rsp->_body));
    if (ret == false)
        return;
    std::string mime = Util::ExtMime(req_path); // 通过扩展名来获取它的mime
    rsp->SetHeader("Content-Type", mime);
    return;
    DBG_LOG("路径：%s", req._path.c_str());
}

void HttpServer::Route(HttpRequest &req, HttpResponse *rsp)
{
    // 对请求进行分析，是一个静态资源请求还是一个功能性请求
    //    静态资源请求，则进行静态资源的处理
    //    功能性请求， 则需要通过几个请求路由表来确定是否有处理函数
    //    既不是静态资源请求，也没有设置对应的功能请求处理函数，就返回405
    if (IsFileHandler(req) == true)
    {
        return FileHandler(req, rsp);
    }
    if (req._path == "/shizerui")
    {
        return name(req, rsp);
    }
    // 走到这里，说明是功能性请求
    if (req._method == "GET" || req._method == "HEAD")
        return Dispatcher(req, rsp, _get_route);
    else if (req._method == "POST")
        return Dispatcher(req, rsp, _post_route);
    else if (req._method == "PUT")
        return Dispatcher(req, rsp, _put_route);
    else if (req._method == "DELETE")
        return Dispatcher(req, rsp, _delete_route);
    rsp->_statu = 405;
}

// 设置上下文
void HttpServer::OnConnected(const PtrConnection &conn)
{
    conn->SetContext(HttpContext());
    DBG_LOG("NEW CONNECTION %p", conn.get());
}

// 缓冲区数据解析 + 处理
void HttpServer::OnMessage(const PtrConnection &conn, Buffer *buffer)
{
    while (buffer->ReadAbleSize() > 0)
    {
        // 1. 获取上下文
        HttpContext *context = conn->GetContext()->get<HttpContext>();
        // 2. 通过上下文对缓冲区数据进行解析，得到HttpRequest对象
        //   a. 如果缓冲区的数据解析出错，就直接回复出错响应
        //   b. 如果解析正常，且请求已经获取完毕，才开始进行处理
        context->RecvHttpRequest(buffer);
        HttpRequest &req = context->Request();
        HttpResponse rsp(context->RespStatu());
        if (context->RespStatu() >= 400) // 代表数据解析出错
        {
            // 进行错误响应，关闭连接
            ErrorHandler(req, &rsp);       // 填充一个错误显示页面数据到rsp中
            WriteResponse(conn, req, rsp); // 组织响应发送给客户端
            context->ReSet();
            buffer->MoveReadOffset(buffer->ReadAbleSize()); // 出错了就把缓冲区数据清空    ？？？？？
            conn->ShutDown();                               // 关闭连接
            return;
        }
        if (context->_recv_statu != RECV_HTTP_OVER) // 代表这不是一个完整的请求
            return;
        // 3. 路由请求 + 业务处理
        Route(req, &rsp);
        // 4. 对HttpResponse进行组织发送
        WriteResponse(conn, req, rsp);
        // 5. 重置上下文
        context->ReSet();
        // 6. 根据长短连接判断是否关闭连接或者继续处理 -- 短连接：接收处理一次服务器就把链接关闭
        //                                               长连接：一直与你通信
        if (rsp.Close() == true)
            conn->ShutDown();
    }
}

HttpServer::HttpServer(int port, int timeout)
    : _server(port)
{
    _server.EnableInactiveRelease(timeout);
    _server.SetConnectedCallback(std::bind(&HttpServer::OnConnected, this, std::placeholders::_1));
    _server.SetMessageCallback(std::bind(&HttpServer::OnMessage, this, std::placeholders::_1, std::placeholders::_2));
}

void HttpServer::SetBaseDir(const std::string &path)
{
    assert(Util::IsDirectory(path) == true);
    _basedir = path;
}

// 设置/添加，请求与处理函数的映射关系
void HttpServer::Get(const std::string &pattern, const Handler &handler)
{
    _get_route.push_back(std::make_pair(std::regex(pattern), handler));
}

void HttpServer::Post(const std::string &pattern, const Handler &handler)
{
    _post_route.push_back(std::make_pair(std::regex(pattern), handler));
}

void HttpServer::Put(const std::string &pattern, const Handler &handler)
{
    _put_route.push_back(std::make_pair(std::regex(pattern), handler));
}

void HttpServer::Delete(const std::string &pattern, const Handler &handler)
{
    _delete_route.push_back(std::make_pair(std::regex(pattern), handler));
}

void HttpServer::SetThreadCount(int count)
{
    _server.SetThreadCount(count);
}

void HttpServer::Listen()
{
    _server.Start();
}